/************************************************************************

								Heroes III
					  Copyright 1999, The 3DO Company

	------------------------------------------------------------------
  						 clone_on_write_ptr.h

	$Header: /Heroes4/clone_on_write_ptr.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( CLONE_ON_WRITE_PTR_H_INCLUDED )
#define CLONE_ON_WRITE_PTR_H_INCLUDED

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

#pragma warning( push )
#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include <cassert>
#include <algorithm>
#include <functional>
#include <memory>

#include "owned_ptr.h"

/////////////////////////////////////////////////////////////////////////////
// t_cloner class template

template < typename t_whatever >
class t_cloner :
	public	std::unary_function<
				t_whatever const &,
				std::auto_ptr< t_whatever > >
{
public:
	std::auto_ptr< t_whatever > operator()( t_whatever const & whatever ) const;
};

template < typename t_whatever >
inline std::auto_ptr< t_whatever > t_cloner< t_whatever >::operator()(
	t_whatever const & whatever ) const
{
	return whatever.clone();
}

/////////////////////////////////////////////////////////////////////////////
// t_clone_on_write_ptr_body class template - auxillary type used by
// t_clone_on_write_ptr class template

template < typename t_elem, typename t_clone_func >
class t_clone_on_write_ptr_body
{
public:
	// Data members
	unsigned				count;
	t_owned_ptr< t_elem >	elem_ptr;
	t_clone_func			clone_func;

	// Constructors
	explicit t_clone_on_write_ptr_body(
		t_clone_func const &	new_clone_func );
	t_clone_on_write_ptr_body(
		std::auto_ptr< t_elem >	new_elem_ptr,
		t_clone_func const &	new_clone_func );
};

template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr_body< t_elem, t_clone_func >::t_clone_on_write_ptr_body(
	t_clone_func const &	new_clone_func )
	:	count( 1 ),
		clone_func( new_clone_func )
{
}

template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr_body< t_elem, t_clone_func >::t_clone_on_write_ptr_body(
	std::auto_ptr< t_elem >	new_elem_ptr,
	t_clone_func const &	new_clone_func )
	:	count( 1 ),
		elem_ptr( new_elem_ptr ),
		clone_func( new_clone_func )
{
}

/////////////////////////////////////////////////////////////////////////////
// t_clone_on_write_ptr class template - Each instance of a
// t_clone_on_write_ptr 'logically' owns and points to its own instance of its
// element type.  However, t_clone_on_write_ptr's implement lazy copying.
// Copying of the element is deferred until a non-const access forces a
// unique copy.

template < typename t_elem, typename t_clone_func = t_cloner< t_elem > >
class t_clone_on_write_ptr
{
public:
	// Types
	typedef t_elem element_type;

	// Constructors/Destructor
	explicit t_clone_on_write_ptr(
		t_clone_func const &	clone_func = t_clone_func() );
	explicit t_clone_on_write_ptr(
		std::auto_ptr< t_elem >	initial_elem_ptr,
		t_clone_func const &	clone_func = t_clone_func() );
	t_clone_on_write_ptr( t_clone_on_write_ptr const & other );
	~t_clone_on_write_ptr();

	// Member functions
	t_elem *				get();
	t_elem const *			get() const { return m_body_ptr->elem_ptr.get(); }
	t_elem const *			get_const() const { return get(); }

	std::auto_ptr< t_elem >	release();

	void					reset(
								std::auto_ptr< t_elem >	new_elem_ptr = std::auto_ptr< t_elem >() );

	void					swap( t_clone_on_write_ptr & other );

	// Operators
	t_clone_on_write_ptr &	operator=( t_clone_on_write_ptr const & right );

	t_elem &				operator*() { return *get(); }
	t_elem const &			operator*() const { return *get(); }
	t_elem *				operator->() { return get(); }
	t_elem const *			operator->() const { return get(); }

private:
	// Types
	typedef t_clone_on_write_ptr_body< t_elem, t_clone_func > t_body;

	// Data members
	t_body *	m_body_ptr;

	// Member functions
	void split();
};

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr< t_elem, t_clone_func >::t_clone_on_write_ptr(
	t_clone_func const & clone_func )
	:	m_body_ptr( new t_body( clone_func ) )
{
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr< t_elem, t_clone_func >::t_clone_on_write_ptr(
	std::auto_ptr< t_elem >	initial_elem_ptr,
	t_clone_func const &	clone_func )
	:	m_body_ptr( new t_body( initial_elem_ptr, clone_func ) )
{
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr< t_elem, t_clone_func >::t_clone_on_write_ptr(
	t_clone_on_write_ptr const & other )
	:	m_body_ptr( other.m_body_ptr )
{
	++m_body_ptr->count;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr< t_elem, t_clone_func >::~t_clone_on_write_ptr()
{
	if ( --m_body_ptr->count == 0 )
		delete m_body_ptr;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_clone_on_write_ptr< t_elem, t_clone_func > &
t_clone_on_write_ptr< t_elem, t_clone_func >::operator=(
	t_clone_on_write_ptr const & right )
{
	++right.m_body_ptr->count;
	if ( --m_body_ptr->count == 0 )
		delete m_body_ptr;
	m_body_ptr = right.m_body_ptr;
	return *this;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline t_elem * t_clone_on_write_ptr< t_elem, t_clone_func >::get()
{
	if ( m_body_ptr->elem_ptr.get() != 0 && m_body_ptr->count > 1 )
		split();

	return m_body_ptr->elem_ptr.get();
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline std::auto_ptr< t_elem > t_clone_on_write_ptr< t_elem, t_clone_func >::release()
{
	if ( m_body_ptr->elem_ptr.get() == 0 )
		return std::auto_ptr< t_elem >();

	if ( m_body_ptr->count > 1 )
		split();

	std::auto_ptr< t_elem > result( m_body_ptr->elem_ptr.release() );
	return result;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
void t_clone_on_write_ptr< t_elem, t_clone_func >::reset(
	std::auto_ptr< t_elem >	new_elem_ptr )
{
	if ( m_body_ptr->elem_ptr.get() == 0 && new_elem_ptr.get() == 0 )
		return;

	if ( m_body_ptr->count > 1 )
	{
		t_body * new_body_ptr = new t_body( new_elem_ptr, m_body_ptr->clone_func );
		--m_body_ptr->count;
		m_body_ptr = new_body_ptr;
	}
	else
		m_body_ptr->elem_ptr = new_elem_ptr;
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
inline void t_clone_on_write_ptr< t_elem, t_clone_func >::swap( t_clone_on_write_ptr & other )
{
	std::swap( m_body_ptr, other.m_body_ptr );
}

/////////////////////////////////////////////////////////////////////////////
template < typename t_elem, typename t_clone_func >
void t_clone_on_write_ptr< t_elem, t_clone_func >::split()
{
	assert( m_body_ptr->count > 1 );
	assert( m_body_ptr->elem_ptr.get() != 0 );

	std::auto_ptr< t_elem > new_elem_ptr = m_body_ptr->clone_func( *m_body_ptr->elem_ptr );
	t_body * new_body_ptr = new t_body( new_elem_ptr, m_body_ptr->clone_func );
	--m_body_ptr->count;
	m_body_ptr = new_body_ptr;
}

#pragma warning( pop )

#endif // !defined( CLONE_ON_WRITE_PTR_H_INCLUDED )
